﻿using System;
using System.Collections.Generic;
using System.Linq;
using NAudio.Dsp;

namespace Orpheus.Analyzer
{
    public class TempoAnalyzer: TrackAnalyserBase
    {
        private List<double> energies;
        private int _sPos = 0;
        private double _eAverage = 0;
        private List<double> _timeBeats;
        private float[] samples;
        private Complex[] _fftBuffer;
        private int _fftPos;
        public Queue<float>[] EsHistory;
        private int _samplesCount;

        public TempoAnalyzer(TrackInfo trackInfo, GetCurrentTimeDelegate timeCallback) 
            : base(trackInfo, timeCallback)
        {
            energies = new List<double>();
            _timeBeats = new List<double>();
            samples = new float[512 * 2];
            _fftBuffer = new Complex[1024];
            EsHistory = new Queue<float>[32];
            for(int i = 0; i < 32; i++)
            {
                EsHistory[i] = new Queue<float>();
            }
        }

        public override void ProcessSamples(float left, float right)
        {
            _samplesCount++;
            //samples[_samplesCount] = left;
            //samples[_samplesCount + 1] = right;
            _eAverage += (left*left + right*right);

            if (_samplesCount % 512 == 0)
            {
                energies.Add(_eAverage);
                _eAverage = 0;

                if (energies.Count == 42)
                {

                    PeakFinder peakFinder = new PeakFinder();
                    var peakPos = peakFinder.DetectPeak(energies.ToArray(), 0, 41);

                    if ((peakPos > 1e-6))
                    {
                        int pos = _samplesCount/512 - 42 + peakPos;
                        TimeSpan time = ConvertSamplesToTime(512*pos);
                        _timeBeats.Add(time.TotalSeconds);
                    }

                    energies.RemoveRange(0, peakPos + 1);
                }
            }
        }

        public override void OnTrackStopped()
        {
            //for (int i = 0; i < energies.Count - 43; i++ )
            //{
            //    var enegrySet = energies.Skip(i).Take(43).ToArray();
            //    double instantAverage = enegrySet.Sum() / enegrySet.Count();

            //    double v = enegrySet.Sum(x => Math.Pow(x - instantAverage, 2)) / enegrySet.Count();

            //    double sensitivity = 1.1;//(-0.0025714 * v) + 1.5142857;

            //    double factor = instantAverage * sensitivity;

            //    if (energies[i] > factor && factor > 0)
            //    {
            //        _timeBeats.Add((i + 1) * 1024 / 44100.0);
            //    }

            //}

            int count = _samplesCount;

            List<double> diffs = new List<double>();
            for(int i = 0; i < _timeBeats.Count - 1; i++)
            {
                diffs.Add(_timeBeats[i + 1] - _timeBeats[i]);
            }

            double beatAverage = diffs.Sum() / diffs.Count;

            _trackInfo.Tempo = new TimeStepFunction<double>();
            _trackInfo.Tempo.SetValueForInterval(60.0 / beatAverage, new TimeInterval(new TimeSpan(0), new TimeSpan(10)));
        }

        private TimeSpan ConvertSamplesToTime(int samples)
        {
            double secs = samples / 44100.0;
            double msecs = secs % 1;

            int fullSeconds = (int)(secs - msecs);

            msecs *= 1000;
            int fullMSeconds = (int)(msecs - msecs % 1);

            return new TimeSpan(0, 0, 0, fullSeconds, fullMSeconds);
        }
    }
}
